package com.chiang.apntest.manager;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.search.UserSearch;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.chiang.apntest.common.Constant;
import com.chiang.apntest.model.LoginConfig;
import com.chiang.apntest.util.NetworkUtil;
import com.chiang.apntest.util.PropertiesUtil;


/**
 * 
 * @author Chiang.C
 * XmppConnManager为单例
 * 主要功能：管理与XMPP服务器的连接；与服务器进行连接，连接成功后，进行登录。
 */
public class XmppConnManager {

	protected static final String TAG = "XmppConnManager";
	private static XmppConnManager instance;
	private static ConnectionConfiguration mConnectionConfiguration;
	private XMPPConnection mXmppConnection;//make it the only-one connection
	private Context mContext;
	private ExecutorService mPool;
	private String mXmppHost;
	private int mXmppPort;
	/**
	 * [请求队列]
	 */
	private BlockingQueue<Entry> reqConnectQueue = new ArrayBlockingQueue<Entry>(10);
	
	// 监听消息
	PacketListener mPacketListener = new PacketListener() {
		@Override
		public void processPacket(Packet packet) {
			Message message = (Message) packet;
			Log.i(TAG, "-------Message from host: " + message.getBody());
			Intent intent=new Intent(Constant.NEW_SYS_MESSAGE_ACTION);
			intent.putExtra("message", message.getBody());
			mContext.sendBroadcast(intent);
		}
	};
		
	private XmppConnManager(){
		
	}
	
	public static XmppConnManager getInstance(){
		if(null==instance){
			instance=new XmppConnManager();
		}
		return instance;
	}
	
	/**
	 * 初始化XMPPConnection
	 * @param loginConfig
	 * @return XMPPConnection
	 */
	/*public XMPPConnection initConnection(LoginConfig loginConfig){
		Connection.DEBUG_ENABLED = false;
		addProviders();
		initConnectionConfig(loginConfig);
		mXmppConnection=new XMPPConnection(mConnectionConfiguration);
		return mXmppConnection;
	}*/
	
	public void initXmppManager(Context context){
		this.mContext=context;
		InputStream is;
		PropertiesUtil propertiesUtil=null;
		try {
			is = context.getAssets().open("xmpp_config.properties");
			propertiesUtil=PropertiesUtil.readProperties(is);
		} catch (IOException e) {
			Log.e(TAG, "-------initXmppManageer: got error-->"+e.getMessage());
		}
		this.mXmppHost=propertiesUtil.getString("xmpp_host", "127.0.0.1");
		this.mXmppPort=propertiesUtil.getInteger("xmpp_port", 5222);
		mPool= Executors.newCachedThreadPool();
		addProviders();
	}
	
	public void setXmppConnection(XMPPConnection xmppConnection){
		this.mXmppConnection=xmppConnection;
	}
	
	public PacketListener getPacketListener(){
		return mPacketListener;
	}
	
	/**
	 * 作用是:
	 */
	private void addProviders() {
		ProviderManager pm = ProviderManager.getInstance();
		// Private Data Storage
		pm.addIQProvider("query", "jabber:iq:private",
				new PrivateDataManager.PrivateDataIQProvider());
		// Time
		try {
			pm.addIQProvider("query", "jabber:iq:time",
					Class.forName("org.jivesoftware.smackx.packet.Time"));
		} catch (ClassNotFoundException e) {
			Log.w("TestClient","Can't load class for org.jivesoftware.smackx.packet.Time");
		}
		// Roster Exchange
		pm.addExtensionProvider("x", "jabber:x:roster",new RosterExchangeProvider());
		// Message Events
		pm.addExtensionProvider("x", "jabber:x:event",new MessageEventProvider());
		// Chat State
		pm.addExtensionProvider("active",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("composing",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("paused",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("inactive",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		pm.addExtensionProvider("gone",
				"http://jabber.org/protocol/chatstates",
				new ChatStateExtension.Provider());
		// XHTML
		pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im",
				new XHTMLExtensionProvider());
		// Group Chat Invitations
		pm.addExtensionProvider("x", "jabber:x:conference",
				new GroupChatInvitation.Provider());
		// Service Discovery # Items
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#items",
				new DiscoverItemsProvider());
		// Service Discovery # Info
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#info",
				new DiscoverInfoProvider());
		// Data Forms
		pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
		// MUC User
		pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user",
				new MUCUserProvider());
		// MUC Admin
		pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin",
				new MUCAdminProvider());
		// MUC Owner
		pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner",
				new MUCOwnerProvider());
		// Delayed Delivery
		pm.addExtensionProvider("x", "jabber:x:delay",
				new DelayInformationProvider());
		// Version
		try {
			pm.addIQProvider("query", "jabber:iq:version",
					Class.forName("org.jivesoftware.smackx.packet.Version"));
		} catch (ClassNotFoundException e) {
			// Not sure what's happening here.
		}
		// VCard
		pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
		// Offline Message Requests
		pm.addIQProvider("offline", "http://jabber.org/protocol/offline",
				new OfflineMessageRequest.Provider());
		// Offline Message Indicator
		pm.addExtensionProvider("offline",
				"http://jabber.org/protocol/offline",
				new OfflineMessageInfo.Provider());
		// Last Activity
		pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
		// User Search
		pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
		// SharedGroupsInfo
		pm.addIQProvider("sharedgroup",
				"http://www.jivesoftware.org/protocol/sharedgroup",
				new SharedGroupsInfo.Provider());
		// JEP-33: Extended Stanza Addressing
		pm.addExtensionProvider("addresses",
				"http://jabber.org/protocol/address",
				new MultipleAddressesProvider());
		// FileTransfer
		pm.addIQProvider("si", "http://jabber.org/protocol/si",
				new StreamInitiationProvider());
		pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams",
				new BytestreamsProvider());
		// Privacy
		pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
		pm.addIQProvider("command", "http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider());
		pm.addExtensionProvider("malformed-action",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.MalformedActionError());
		pm.addExtensionProvider("bad-locale",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadLocaleError());
		pm.addExtensionProvider("bad-payload",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadPayloadError());
		pm.addExtensionProvider("bad-sessionid",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.BadSessionIDError());
		pm.addExtensionProvider("session-expired",
				"http://jabber.org/protocol/commands",
				new AdHocCommandDataProvider.SessionExpiredError());

	
	}
	
	/**
	 * ConnectionConfiguration(String host, int port, String serviceName)
	 * Creates a new ConnectionConfiguration using the specified host,
	 * port and service name.
	 * @param loginConfig
	 */
	//TODO
	private void initConnectionConfig(LoginConfig loginConfig) {
		mConnectionConfiguration = new ConnectionConfiguration(
				loginConfig.getXmppHost(), loginConfig.getXmppPort());
		mConnectionConfiguration.setSASLAuthenticationEnabled(false);// 不使用SASL验证，设置为false
		mConnectionConfiguration.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
		// 允许自动连接
		mConnectionConfiguration.setReconnectionAllowed(false);
	}

	public XMPPConnection getXmppConnection(){
		return mXmppConnection;
	}
	
	/**
	 * 是否已经验证
	 * @return
	 */
	public boolean isAuthenticated() {
		return mXmppConnection != null && mXmppConnection.isConnected()
				&& mXmppConnection.isAuthenticated();
	}
	
	/**
	 * 是否已经连接
	 * @return
	 */
	public boolean isConnected(){
		return mXmppConnection != null && mXmppConnection.isConnected();
	}
	
	/**
	 * 【连接服务器并登录】
	 * 设置ConnectionConfiguration各个参数，对XMPPConnection进行初始化；然后连接服务器器(ConnectTask)。
	 * 如果连接成功则进行登录(LoginTask)。
	 * @param username
	 * @param password
	 * @return true connect success,false if failed.
	 */
	public boolean connect(String username,String password){
		boolean result=false;
		try {
			XMPPConnection xmppConnection=mPool.submit(new ConnectTask(null)).get();
			this.mXmppConnection=xmppConnection;
			//如果已连接到xmpp服务器的话就开始登录
			if(null!=xmppConnection && xmppConnection.isConnected()){
				result=login(xmppConnection,username, password);
			}
		} catch (InterruptedException e) {
			Log.e(TAG, "-------connect error:"+e.getMessage());
			result=false;
		} catch (ExecutionException e) {
			Log.e(TAG, "-------connect error:"+e.getMessage());
			result=false;
		}
		return result;
	}
	
	public void reConnect(String name, String password) {
		// TODO Auto-generated method stub
		Log.i(TAG, "-------reConnect...");
	}
	
	private boolean login(XMPPConnection xmppConnection, String username,
			String password) {
		boolean result=false;
		String message;
		try {
			message = mPool.submit(new LoginTask(this, xmppConnection, username, password)).get();
			if(Constant.LOGIN_SUCCESS.equals(message)||Constant.ALREADY_AUTHENTICATED.equals(message)){
				result=true;
			}else{
				result=false;
			}
		} catch (InterruptedException e) {
			Log.e(TAG, "-------connect error:"+e.getMessage());
		} catch (ExecutionException e) {
			Log.e(TAG, "-------connect error:"+e.getMessage());
		}
		return result;
	}

	/**
	 * 断开连接
	 */
	public boolean disconnectToHost(){
		if(isConnected()){
			Log.e(TAG, "-------XmppConnManager:disconnectToHost()");
			reqConnectQueue.clear();
			mXmppConnection.removePacketListener(mPacketListener);
			try {
				mXmppConnection.disconnect();
				mXmppConnection=null;
				return true;
			} catch (Exception e) {
				Log.e(TAG, "-------xmpp disconnect error,caused by:"+e.getMessage());
				return false;
			}
		}
		return true;
	}
	
	public void startThreadForConnecting(){
		Log.i(TAG, "-------startThreadForConnecting()");
		new Thread(new Runnable() {
			@Override
			public void run() {
				while(true){
					try {
						Entry entry=reqConnectQueue.take();
						Log.i(TAG, "-------Take a entry for login, username:"+entry.getName());
						if(mContext!=null&&!NetworkUtil.isNetworkConnected(mContext)){
							continue;
						}
						if(entry!=null){
							if(!connect(entry.getName(), entry.getPassword())){
								reConnect(entry.getName(),entry.getPassword());
							}
						}
					} catch (InterruptedException e) {
						Log.e(TAG, "-------startThreadForConnecting():got error,caused by:"+e.getMessage());
					}
				}
			}
		}).start();
	}
	
	
	/**
	 * [请求连接]<BR>
	 * [功能详细描述]
	 * 
	 * @param username
	 * @param password
	 * 
	 */
	public void submitConnectReq(String username, String password) {
		Log.i(TAG,"-------submitConnectReq for username:{},password:{}"+username+" "+
				password);
		try {
			reqConnectQueue.add(new Entry(username, password));
			
		} catch (Exception e) {
			reqConnectQueue.clear();
			reqConnectQueue.add(new Entry(username, password));
		}
	}
	
	/**
	 * 
	 * @author Chiang.C
	 *
	 */
	private class ConnectTask implements Callable<XMPPConnection>{
		private XMPPConnection xmppConnection;
		
		public ConnectTask(XMPPConnection xmppConnection){
			this.xmppConnection=xmppConnection;
		}
		
		@Override
		public XMPPConnection call() throws Exception {
			if(null==xmppConnection){
				ConnectionConfiguration configuration=new ConnectionConfiguration(mXmppHost, mXmppPort);
				configuration.setSASLAuthenticationEnabled(false);
				configuration.setSecurityMode(SecurityMode.disabled);
				configuration.setRosterLoadedAtLogin(true);
				configuration.setReconnectionAllowed(false);
				xmppConnection = new XMPPConnection(configuration);
			}
			if(!xmppConnection.isConnected()){
				try {
					xmppConnection.connect();
					Log.i("ConnectTask", "-------Xmpp connect success.");
				} catch (Exception e) {
					Log.e("ConnectTask", "-------Xmpp connect failed, caused by: "+e.getMessage());
				}
			}else{
				Log.i("ConnectTask", "-------Xmpp already connected.");
			}
			return xmppConnection;
		}
	}
	
	
	/**
	 * 
	 * @author Chiang.C
	 *
	 */
	private class LoginTask implements Callable<String>{
		
		private XmppConnManager xmppConnManager;
		private XMPPConnection xmppConnection;
		private String username;
		private String password;
		
		public LoginTask(XmppConnManager xmppConnManager,XMPPConnection xmppConnection,String username, String password){
			this.xmppConnManager=xmppConnManager;
			this.xmppConnection=xmppConnection;
			this.username=username;
			this.password=password;
		}
		
		@Override
		public String call() throws Exception {
			//Returns true if currently authenticated by successfully calling the login method.
			if(!xmppConnection.isAuthenticated()){
				try {
					//Logs in to the server using the strongest authentication mode supported by the server, 
					//then sets presence to available.
					xmppConnection.login(username, password);
					Log.i("LoginTask", "-------Login success,【username】:"+username);
					//设置登录成功的连接
					xmppConnManager.setXmppConnection(xmppConnection);
					registerListeners();
					//注册签到
					Presence presence = new Presence(Presence.Type.available, "在线",1, null);
					mXmppConnection.sendPacket(presence);
					return Constant.LOGIN_SUCCESS;
				} catch (Exception e) {
					Log.e("LoginTask", "-------Login to Xmpp failed, caused by:"+e.getMessage());
					return Constant.LOGIN_ERROR;
				}
			}else{
				Log.i("LoginTask", "-------\'"+username+"\' already been authenticated.");
				return Constant.ALREADY_AUTHENTICATED;
			}
		}
		
		/**
		 * 注册了消息监听器
		 */
		private void registerListeners() {
			xmppConnection.addPacketListener(mPacketListener, new MessageTypeFilter(Message.Type.normal));
		}
	}
	
	/**
	 * [连接并登陆]<BR>
	 * 1.首次登陆 2.非首次登陆 　　2.1.同一用户登陆 2.2.非同一用户登陆
	 * 
	 * @param useranme
	 * @param password
	 * @return 2013-7-18 上午10:59:04
	 */

	class Entry {
		public Entry(String name, String password) {
			super();
			this.name = name;
			this.password = password;
		}

		private String name;
		private String password;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getPassword() {
			return password;
		}

		public void setPassword(String password) {
			this.password = password;
		}
	}
	
}
